package com.payment_server.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.payment_server.entity.ConsumptionLogEntity;
import com.payment_server.util.RedisUtil;
import com.payment_server.util.TokenUtil;
import com.payment_server.util.responseUtil.ResponseUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import redis.clients.jedis.Jedis;

import java.util.*;

@RestController
public class PaymentScanCode {
    private Log log = LogFactory.getLog("三岁小仙仙");

    private static final String APPID = "wx9b04d88d2bf7f251";

    private static final String APPSECRET = "8b79027ddf3f0d229c075d7301a219d4";

    @RequestMapping("/wxLogin")
    public Object wxLogin(@RequestBody String body) {
        String code = JSONObject.parseObject(body).getString("code");
        // 获取openid和sessionKey
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + APPID + "&secret=" + APPSECRET + "&js_code=" + code + "&grant_type=authorization_code";
        String post = HttpUtil.post(url, "");

        JSONObject jsonObject = JSONObject.parseObject(post);
        Map<Object, Object> map = new HashMap<>();
        if (jsonObject.containsKey("errcode")) {
            map.put("errcode", jsonObject.getString("errcode"));
            //HttpStatus: http://www.httpstatus.cn/
            return new ResponseUtil<>().CODE_EXPIRE(null, map);
        }

        String md5Hex = DigestUtil.md5Hex(post);

        Jedis redis = RedisUtil.getRedisDS();

        String openid = jsonObject.getString("openid");
        if (!redis.hexists("UserBalance", openid)) {
            redis.hset("UserBalance", openid, String.valueOf(0));
            map.put("balance", 0);
        } else {
            map.put("balance", redis.hget("UserBalance", openid));
        }

        redis.setex(md5Hex, 60 * 60 * 24, jsonObject.toJSONString());
        map.put("token", md5Hex);
        return new ResponseUtil<>().OK(null, map);
    }

    @RequestMapping("/recharge")
    public Object recharge(@RequestBody String body, @RequestHeader("token") String token) {
        JSONObject tokenInfo = TokenUtil.getTokenInfo(token);
        if (tokenInfo.isEmpty()) {
            return new ResponseUtil<>().TOKEN_EXPIRE(null, null);
        }

        String openid = tokenInfo.getString("openid");

        int balance = NumberUtil.parseInt(RedisUtil.getRedisDS().hget("UserBalance", openid));
        balance += JSONObject.parseObject(body).getShort("money");
        RedisUtil.getRedisDS().hset("UserBalance", openid, StrUtil.toString(balance));
        tokenInfo.clear();
        tokenInfo.put("balance", balance);
        return new ResponseUtil<>().OK(null, tokenInfo);
    }

    @RequestMapping("/account/refresh")
    public Object accountRef(@RequestHeader("token") String token) {
        JSONObject tokenInfo = TokenUtil.getTokenInfo(token);
        if (tokenInfo.isEmpty()) return new ResponseUtil<>().TOKEN_EXPIRE(null, null);
        String openid = tokenInfo.getString("openid");
        String balance = RedisUtil.getRedisDS().hget("UserBalance", openid);
        return new ResponseUtil<>().OK(null, balance);
    }

    @RequestMapping("/order/pay")
    public Object orderPay(@RequestBody String body, @RequestHeader("token") String token) {
        JSONObject tokenInfo = TokenUtil.getTokenInfo(token);
        //验证token
        if (tokenInfo.isEmpty()) return new ResponseUtil<>().TOKEN_EXPIRE(null, null);

        String order = JSONObject.parseObject(body).getString("order");

        JSONObject jsonObject = JSONObject.parseObject(order);
        StringBuffer sb = new StringBuffer();
        sb.append(jsonObject.getString("UUID"));
        sb.append(jsonObject.getString("payPrice"));
        sb.append(jsonObject.getString("createDate"));
        sb.append(jsonObject.getString("expireMinutes"));
        String md5Hex = DigestUtil.md5Hex(sb.toString());

        Jedis redis = RedisUtil.getRedisDS();

        //验证数据完整性
        if (!md5Hex.equals(jsonObject.getString("hashCode")) && md5Hex.equals(redis.get(jsonObject.getString("UUID"))))
            return new ResponseUtil<>().MD5_VERIFY_FAIL(null, null);

        //验证订单在后台是否存在（过期）
        if (!redis.exists(jsonObject.getString("UUID")))
            return new ResponseUtil<>().ORDER_EXPIRE(null, null);

        String balance = redis.hget("UserBalance", tokenInfo.getString("openid"));
        float userBalance = NumberUtil.parseFloat(balance),
                payPrice = NumberUtil.parseFloat(jsonObject.getString("payPrice"));

        double sub = NumberUtil.sub(userBalance, payPrice);

        if (sub < 0) return new ResponseUtil<>().ORDER_EXPIRE("余额不足", null);

        redis.hset("UserBalance", tokenInfo.getString("openid"), StrUtil.toString(sub));
        redis.del(jsonObject.getString("UUID"));
        createConsumptionLog(jsonObject, tokenInfo.getString("openid"));

        return new ResponseUtil<>().OK("支付成功", sub);
    }

    private void createConsumptionLog(JSONObject order, String openid) {
        ConsumptionLogEntity cle = new ConsumptionLogEntity();
        cle.setUUID(order.getString("UUID"));
        cle.setPayPrice(order.getString("payPrice"));
        cle.setCreateDate(order.getString("createDate"));
        cle.setPayDate(DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss"));

        /**
         * 方案一、取openid前8位 + # + 随机数 = key
         */
        /*String key = new StringBuffer().append(openid.substring(0, 8))
                .append("#")
                .append(RandomUtil.randomInt(999)).toString();*/

        /**
         * 方案一、openid前8位 + # + UUID前4位 + # + 支付（当前）日期 = key
         *
         * UUID前4位：区分每一笔订单。因为同一天可能有多笔订单，加上UUID可以区分订单，订单号唯一
         * 支付（当前）日期：后台刷新不需查询所有记录，查询当天即可
         *
         * 前端缓存为空时（首次访问），请求后台。之后前端会设置缓存，不会每次都请求后台，直至用户手动刷新
         * 请求后台查询用户订单，只需要查询当天的即可，不需要全重新查询。前端再做重复数据处理
         * 匹配模式 openidz#*#支付（当前）日期
         */

        String key = new StringBuffer().append(openid.substring(0, 8))
                .append("#")
                .append(order.getString("UUID").substring(0, 4))
                .append("#")
                .append(DateUtil.format(DateUtil.date(), "yyyyMMdd")).toString();

        Jedis redisDS = RedisUtil.getRedisDS();
        redisDS.select(1);
        redisDS.setex(key, 60 * 60 * 24 * 7, JSON.toJSONString(cle));
    }

    @RequestMapping("/account/consumptionLog")
    public Object consumptionLog(@RequestParam("first") int first, @RequestHeader("token") String token) {
        JSONObject tokenInfo = TokenUtil.getTokenInfo(token);
        //验证token
        if (tokenInfo.isEmpty()) return new ResponseUtil<>().TOKEN_EXPIRE(null, null);
        String openid = tokenInfo.getString("openid").substring(0, 8);

        Jedis redisDS = RedisUtil.getRedisDS();
        redisDS.select(1);

        //用户第一次获取数据，需要返回所有记录
        Set<String> keys = new HashSet<>();
        if (first == 1) {
             keys = redisDS.keys(openid + "*");
        } else {
            String match = new StringBuffer().append(openid)
                    .append("#").append("*").append("#")
                    .append(DateUtil.format(DateUtil.date(), "yyyyMMdd")).toString();
            keys = redisDS.keys(match);
        }
        List<JSON> entityList = new ArrayList<>();
        for (String val : keys) {
            entityList.add(JSON.parseObject(redisDS.get(val)));
        }

        return new ResponseUtil<>().OK(null, entityList);
    }

}
